package cli.command;

import cli.view.TextEffects;

import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.NotDirectoryException;
import java.nio.file.Path;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Comparator;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Predicate;

public class ListContent implements Command {
    private PrintWriter out;
    private Path currentPath;
    private boolean a = false; // 是否显示隐藏文件
    private boolean d = false; // 是否只显示文件夹
    private boolean l = false; // 是否以长列表的形式显示文件和目录的详细信息
    private boolean r = false; // 倒序
    private boolean t = false; // 是否按照修改时间排序（越新的排在越前面）
    private boolean S = false; // 是否按照文件大小排序（越大的排在越前面）

    public ListContent(PrintWriter out, Path currentPath) {
        this.out = out;
        this.currentPath = currentPath;
    }

    private void setOptions(String options) {
        a = options.contains("a");
        d = options.contains("d");
        l = options.contains("l");
        r = options.contains("r");
        t = options.contains("t");
        S = options.contains("S");
    }

    private Predicate<Path> getPredicate() {
        Predicate<Path> predicate = path -> true;
        if (!a) {
            predicate = predicate.and(p -> {
                        try {
                            return !Files.isHidden(p) && !p.getFileName().toString().startsWith(".");
                        } catch (IOException e) {
                            return false;
                        }
                    });
        }
        if (d) {
            predicate = predicate.and(p -> {
                try {
                    return Files.isDirectory(p);
                } catch (Exception e) {
                    return false;
                }
            });
        }
        return predicate;
    }

    private Comparator<Path> getComparator() {
        Comparator<Path> comparator = Comparator.naturalOrder();
        if (t) {
            comparator = (p1, p2) -> {
                try {
                    return -Files.getLastModifiedTime(p1).compareTo(Files.getLastModifiedTime(p2));
                } catch (IOException e) {
                    return 0;
                }
            };
        } else if (S) {
            comparator = (p1, p2) -> {
                try {
                    return -Long.compare(Files.size(p1), Files.size(p2));
                } catch (IOException e) {
                    return 0;
                }
            };
        }
        if (r) {
            comparator = comparator.reversed();
        }
        return comparator;
    }

    private Consumer<Path> getConsumer() {
        if (l) {
            return p -> {
                try {
                    String s = (Files.isDirectory(p) ? "d" : "-") +
                            (Files.isReadable(p) ? 'r' : '-') +
                            (Files.isWritable(p) ? 'w' : '-') +
                            (Files.isExecutable(p) ? 'x' : '-') +
                            '\t' +
                            String.format("%-30s\t", Files.getOwner(p).getName()) +
                            String.format("%10d\t", Files.size(p)) +
                            DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm\t").format(Files.getLastModifiedTime(p).toInstant().atZone(ZoneId.systemDefault())) +
                            (Files.isDirectory(p) ? TextEffects.apply(p.getFileName().toString(), TextEffects.BLUE) : Files.isExecutable(p) ? TextEffects.apply(p.getFileName().toString(), TextEffects.GREEN) : TextEffects.apply(p.getFileName().toString(), TextEffects.BLACK));
                    out.println(s);
                } catch (IOException e) {
                    return;
                }
            };
        } else {
            AtomicInteger count = new AtomicInteger(0);
            return p -> {
                try {
                    String name = Files.isDirectory(p) ? TextEffects.apply(p.getFileName().toString(), TextEffects.BLUE) : Files.isExecutable(p) ? TextEffects.apply(p.getFileName().toString(), TextEffects.GREEN) : TextEffects.apply(p.getFileName().toString(), TextEffects.BLACK);
                    if (count.incrementAndGet() % 4 == 0) {
                        out.printf("%-60s\n", name);
                    } else {
                        out.printf("%-60s\t", name);
                    }
                } catch (Exception e) {
                    return;
                }
            };
        }
    }

    @Override
    public void handle(String[] args) throws Exception {
        int pos = 0;
        if (pos < args.length) {
            if (args[pos].startsWith("-")) {
                setOptions(args[pos++]);
            }
        }
        Path path;
        if (pos < args.length) {
            try {
                path = currentPath.resolve(args[pos]);
            } catch (InvalidPathException e) {
                out.println("路径"+ args[pos] +"无效！");
                return;
            }
            pos++;
        } else {
            path = currentPath;
        }
        if (pos < args.length) {
            out.println("无法解析参数" + args[pos]);
            return;
        }
        try {
            Files.list(path)
                    .filter(getPredicate())
                    .sorted(getComparator())
                    .forEach(getConsumer());
            out.println();
        } catch (NotDirectoryException e) {
            out.println("文件夹不存在！");
            return;
        }
    }
}
